#!/usr/bin/python3
# -*- coding: utf-8 -*-

from collections import Iterable
from datetime import datetime, timedelta
from .validators import reject_if, reject_if_null, reject_if_blank

# 项目未发布过。
PRJ_UNDEPLOYED = 0
# 项目正在发布中。
PRJ_DEPLOYING = 1
# 项目已成功发布。
PRJ_SUCCESSFUL = 2
# 项目发布失败。
PRJ_FAILED = 3


class Project(object):
    """项目。"""
    __slots__ = ('__name', '__title', '__version_number', '__description', '__enabled',
                 '__jobs', '__nodes', '__status', '__last_timestamp', '__last_duration',)

    def __init__(
            self,
            name: str,
            description: str = None,
            version_number: str = None,
            status: int = 0,
            last_timestamp: datetime = None,
            last_duration: timedelta = None,
            jobs: (list, tuple,) = None,
            nodes: (list, tuple, set, str,) = None,
            enabled: bool = True
    ) -> None:
        """ 构造项目对象。

        :param name: 项目名称。
        :param description: 项目描述。
        :param version_number: 项目最新版本。
        :param status: 项目状态。
        :param last_timestamp: 最近发布时间。
        :param last_duration: 最近发布用时。
        :param jobs: 此项目使用的发布任务名称列表。
        :param nodes: 此项目可以发布的节点名称列表。
        :param enabled: 是否启用。
        """
        reject_if_blank(name)

        self.__name = name
        self.__version_number = version_number.strip() if version_number else ''
        self.__description = description if description else ''
        self.__status = status
        self.__last_timestamp = last_timestamp
        self.__last_duration = last_duration
        self.__jobs = tuple(jobs) if jobs else tuple()
        if isinstance(nodes, Iterable):
            self.__nodes = tuple(str(item) for item in nodes) if nodes else tuple()
        elif isinstance(nodes, str):
            self.__nodes = (nodes,)
        else:
            self.__nodes = tuple()
        self.__enabled = enabled

    @property
    def name(self) -> str:
        """项目名称。"""
        return self.__name

    @property
    def version_number(self) -> str:
        """项目当前版本号。"""
        return self.__version_number

    @property
    def description(self) -> str:
        """项目描述。"""
        return self.__description

    @property
    def enabled(self) -> bool:
        """项目是否有效，无效的项目不能参与发布。"""
        return self.__enabled

    @property
    def status(self) -> int:
        """项目状态。"""
        return self.__status

    @property
    def last_timestamp(self) -> datetime:
        """最后发布时间。"""
        return self.__last_timestamp

    @property
    def last_duration(self) -> timedelta:
        """最后发布用时。"""
        return self.__last_duration

    @property
    def jobs(self) -> tuple:
        """关联的任务名称列表"""
        return self.__jobs

    @property
    def nodes(self) -> tuple:
        """关联的目标节点名称列表。"""
        return self.__nodes


class Pkg(object):
    """项目相关的软件包。"""
    __slots__ = ('__prj_name', '__version_number', '__commit', '__timestamp', '__file_path',)

    def __init__(
            self,
            prj_name: str,
            version_number: str,
            commit: str,
            timestamp: datetime,
            file_path: str
    ):
        """ 构造软件包对象。

        :param prj_name: 关联的项目名称。
        :param version_number: 软件包的版本号。
        :param commit: 软件包的提交信息。
        :param timestamp: 生成软件包的时间戳。
        :param file_path: 软件包的本地路径。
        """
        reject_if_blank(prj_name)
        reject_if_blank(version_number)
        reject_if_null(timestamp)
        reject_if_blank(file_path)

        self.__prj_name = prj_name.strip()
        self.__version_number = version_number.strip()
        self.__commit = commit if commit else ''
        self.__timestamp = timestamp
        self.__file_path = file_path.strip()

    @property
    def prj_name(self):
        """关联的项目名称。"""
        return self.__prj_name

    @property
    def version_number(self):
        """软件包的版本号。"""
        return self.__version_number

    @property
    def commit(self):
        """软件包的提交信息。"""
        return self.__commit

    @property
    def timestamp(self):
        """生成软件包的时间戳。"""
        return self.__timestamp

    @property
    def file_path(self):
        """软件包的本地路径。"""
        return self.__file_path


class Node(object):
    """发布的目标节点。"""
    __slots__ = ('__name', '__description', '__hostname', '__bundle', '__enabled', '__props',)

    def __init__(
            self,
            name: str,
            description: str,
            hostname: str,
            bundle: str,
            enabled: bool = True,
            props: dict = None
    ):
        """ 构造目标节点对象。

        :param name: 节点名称。
        :param description: 节点描述。
        :param hostname: 节点主机名。
        :param bundle: 节点绑定类型。
        :param enabled: 节点是否有效。
        :param props: 节点的参数集合。
        """
        reject_if_blank(name)
        reject_if_blank(hostname)
        reject_if_blank(bundle)
        reject_if_null(enabled)

        self.__name = name.strip()
        self.__description = description if description else ''
        self.__hostname = hostname.strip()
        self.__bundle = bundle.strip()
        self.__enabled = enabled
        self.__props = dict()
        if props:
            self.__props.update(props)

    @property
    def name(self) -> str:
        """节点名字。"""
        return self.__name

    @property
    def hostname(self) -> str:
        """节点地址。"""
        return self.__hostname

    @property
    def bundle(self) -> str:
        """节点的绑定类别。"""
        return self.__bundle

    @property
    def description(self) -> str:
        """节点描述。"""
        return self.__description

    @property
    def enabled(self) -> bool:
        """节点是否有效，无效的节点不能参与发布。"""
        return self.__enabled

    @property
    def props(self) -> dict:
        """ 输出节点的所有参数。"""
        return dict(self.__props)

    def __str__(self) -> str:
        return 'Node(name={name},hostname={hostname})'.format(name=repr(self.name), hostname=repr(self.hostname))


class Job(object):
    """发布任务。

    每个任务包含若干命令，执行任务时，每个命令被依次执行。
    """
    __slots__ = ('__type', '__description', '__params', '__commands',)

    def __init__(
            self,
            type_: str,
            description: str,
            params: Iterable = None,
            commands: Iterable = None
    ):
        """ 构造任务对象。

        :param type_: 任务的类型。
        :param description: 任务的描述。
        :param params: 任务参数列表。列表中的每个元素都是`tuple`类型，该`tuple`包含3个元素，分别表示参数的名字、参数的类型、参数是否必填和参数的描述。
        :param commands: 任务包含的命令列表，列表中的每个元素都是`dict`类型，该`dict`表示构造一个`Command`所需要的参数。
        """
        reject_if_blank(type_)
        reject_if(lambda: not commands)

        self.__type = type_.strip()
        self.__description = description if description else ''
        self.__params = tuple(params) if params else tuple()
        self.__commands = tuple(commands)

    @property
    def type_(self) -> str:
        """任务类型。"""
        return self.__type

    @property
    def description(self) -> str:
        """任务描述。"""
        return self.__description

    @property
    def params(self) -> tuple:
        """任务需要的参数。"""
        return self.__params

    @property
    def commands(self) -> tuple:
        """任务包含的命令。"""
        return self.__commands
